const fs = require("fs");
const path = require("path");
const Joi = require("joi");
const base = require("./../api/base");
const HomeBase = require("./../home/base");
const { Payment } = require("wechat-pay");

function mul(a, b) {
    let c = 0;
    const d = a.toString();
    const e = b.toString();
    try {
        c += d.split(".")[1].length;
    } catch (f) {}
    try {
        c += e.split(".")[1].length;
    } catch (f) {}
    return (
        Number(d.replace(".", "")) *
        Number(e.replace(".", "")) /
        Math.pow(10, c)
    );
}

module.exports = class extends base {
    async _before_index() {
        await super.isWxaAuth();
    }

    /**
     *
     * @api {get} /pay/wxpay/index 微信支付
     * @apiDescription 微信支付
     * @apiGroup Pay
     * @apiVersion 0.0.1
     *
     * @apiHeader {String} Referer 小程序请求referer
     *
     * @apiParam {Number} id 账单ID
     *
     * @apiSampleRequest /pay/wxpay/index
     *
     */
    async index() {
        const { id } = this.query;
        const valid = await this.validate(
            { id: id },
            { id: Joi.number().required() }
        );
        if (!valid) {
            this.fail("参数有误");
            return;
        }

        const notifyUrl = process.env.APP_HOST + "/app/pay/wxpay/notify";
        const wxPay = await this.model("wxa_pay")
            .query({
                where: {
                    wxa_id: this.state.wxa.id
                }
            })
            .fetch();
        if (!wxPay || !wxPay.mchid || !wxPay.key || !wxPay.pfx) {
            this.fail("支付参数有误");
            return;
        }

        const payment = new Payment({
            partnerKey: wxPay.key,
            appId: this.state.wxa.authorizer_appid,
            mchId: wxPay.mchid,
            notifyUrl: notifyUrl,
            pfx: Buffer.from(JSON.parse(wxPay.pfx).data)
        });

        const trade = await this.model("trade")
            .query({ where: { id: id, status: 0 } })
            .fetch({ withRelated: ["user"] });
        if (!trade) {
            this.fail("账单不存在或者已支付");
            return;
        }

        try {
            const payargs = await new Promise((resolve, reject) => {
                payment.getBrandWCPayRequestParams(
                    {
                        body: trade.tradeid,
                        out_trade_no: trade.tradeid,
                        total_fee: mul(trade.money, 100),
                        spbill_create_ip: "127.0.0.1",
                        openid: trade.user.openId,
                        trade_type: "JSAPI"
                    },
                    (err, payargs) => {
                        if (err) return reject(err);
                        resolve(payargs);
                    }
                );
            });

            this.success(payargs, "获取支付参数成功");
        } catch (err) {
            this.fail(err.message, "获取支付参数失败");
        }
    }

    async notify() {
        const data = this.xml;
        if (data && data.return_code === "SUCCESS") {
            this.payTrue(data.out_trade_no);
            this.view("success");
        } else {
            this.view("fail");
        }
    }

    async _before_qrcode() {
        await super.isWxaAuth();
    }

    /**
     *
     * @api {get} /pay/wxpay/qrcode 微信支付二维码
     * @apiDescription 微信支付二维码
     * @apiGroup Pay
     * @apiVersion 0.0.1
     *
     * @apiParam {Number} id 账单ID
     *
     * @apiSampleRequest /pay/wxpay/qrcode
     *
     */
    async qrcode() {
        const { id } = this.query;
        const valid = await this.validate(
            { id: id },
            { id: Joi.number().required() }
        );
        if (!valid) {
            this.fail("参数有误");
            return;
        }

        const notifyUrl = process.env.APP_HOST + "/app/pay/wxpay/notify";
        const wxPay = await this.model("wxa_pay")
            .query({
                where: {
                    wxa_id: this.state.wxa.id
                }
            })
            .fetch();
        if (!wxPay || !wxPay.mchid || !wxPay.key || !wxPay.pfx) {
            this.fail("支付参数有误");
            return;
        }

        const payment = new Payment({
            partnerKey: wxPay.key,
            appId: this.state.wxa.authorizer_appid,
            mchId: wxPay.mchid,
            notifyUrl: notifyUrl,
            pfx: Buffer.from(JSON.parse(wxPay.pfx).data)
        });

        const trade = await this.model("trade")
            .query({ where: { id: id, status: 0 } })
            .fetch({ withRelated: ["user"] });
        if (!trade) {
            this.fail("账单不存在或者已支付");
            return;
        }

        try {
            const res = await new Promise((resolve, reject) => {
                payment.unifiedOrder(
                    {
                        body: trade.tradeid,
                        out_trade_no: trade.tradeid,
                        total_fee: mul(trade.money, 100),
                        spbill_create_ip: "127.0.0.1",
                        product_id: trade.tradeid,
                        trade_type: "NATIVE"
                    },
                    (err, res) => {
                        if (err) return reject(err);
                        resolve(res);
                    }
                );
            });

            this.success(res, "获取支付参数成功");
        } catch (err) {
            this.fail(err.message, "生成支付二维码失败");
        }
    }

    async _before_refund() {
        const homeBase = new HomeBase(this.ctx, this.next);
        await homeBase.isWxaAuth();
    }

    /**
     *
     * @api {get} /pay/wxpay/refund 微信支付退款
     * @apiDescription 微信支付退款
     * @apiGroup Pay
     * @apiVersion 0.0.1
     *
     * @apiParam {Number} id 账单ID
     *
     * @apiSampleRequest /pay/wxpay/refund
     *
     */
    async refund() {
        const { id, money } = this.query;
        const valid = await this.validate(
            { id: id },
            { id: Joi.number().required() }
        );
        if (!valid) {
            this.fail("参数有误");
            return;
        }

        const notifyUrl = process.env.APP_HOST + "/app/pay/wxpay/refund_notify";
        const wxPay = await this.model("wxa_pay")
            .query({
                where: {
                    wxa_id: this.state.wxa.id
                }
            })
            .fetch();
        if (!wxPay || !wxPay.mchid || !wxPay.key || !wxPay.pfx) {
            this.fail("支付参数有误");
            return;
        }

        const payment = new Payment({
            partnerKey: wxPay.key,
            appId: this.state.wxa.authorizer_appid,
            mchId: wxPay.mchid,
            notifyUrl: notifyUrl,
            pfx: Buffer.from(JSON.parse(wxPay.pfx).data)
        });

        const trade = await this.model("trade")
            .query({ where: { id: id, status: 1 } })
            .fetch({ withRelated: ["user"] });
        if (!trade) {
            this.fail("账单不存在或者未支付");
            return;
        }

        if (money && parseFloat(money) * 100 > parseFloat(trade.money) * 100) {
            this.fail("退款金额有误");
            return;
        }

        const refund_fee = money ? mul(money, 100) : mul(trade.money, 100);

        try {
            const refund = await new Promise((resolve, reject) => {
                payment.refund(
                    {
                        out_trade_no: trade.tradeid,
                        out_refund_no:
                            trade.tradeid + Math.floor(Math.random() * 50),
                        total_fee: mul(trade.money, 100),
                        refund_fee: refund_fee
                    },
                    (err, payargs) => {
                        if (err) return reject(err);
                        resolve(payargs);
                    }
                );
            });

            this.success(refund, "申请退款成功");
        } catch (err) {
            this.fail(err.message, "申请退款失败");
        }
    }

    async refund_notify() {
        const data = this.xml;
        if (data && data.return_code === "SUCCESS") {
            this.refundTrue(data.out_trade_no);
            this.view("success");
        } else {
            this.view("fail");
        }
    }

    async _before_transfer() {
        const homeBase = new HomeBase(this.ctx, this.next);
        await homeBase.isCustomAuth();
        await homeBase.isWxaAuth();
    }

    /**
     *
     * @api {get} /pay/wxpay/transfer 微信支付转账
     * @apiDescription 微信支付转账
     * @apiGroup Pay
     * @apiVersion 0.0.1
     *
     * @apiParam {Number} id 账单ID
     *
     * @apiSampleRequest /pay/wxpay/transfer
     *
     */
    async transfer() {
        const { partner_trade_no, amount, openid, re_user_name } = this.query;

        const notifyUrl =
            process.env.APP_HOST + "/app/pay/wxpay/transfer_notify";
        const wxPay = await this.model("wxa_pay")
            .query({
                where: {
                    wxa_id: this.state.wxa.id
                }
            })
            .fetch();
        if (!wxPay || !wxPay.mchid || !wxPay.key || !wxPay.pfx) {
            this.fail("支付参数有误");
            return;
        }

        Payment.prototype.transferCreateUnifiedOrder = function(
            params,
            callback
        ) {
            params.mch_appid = this.appId;
            params.mchid = this.mchId;

            this._signedQuery(
                "https://api.mch.weixin.qq.com/mmpaymkttransfers/promotion/transfers",
                params,
                {
                    required: [
                        "partner_trade_no",
                        "amount",
                        "openid",
                        "re_user_name"
                    ],
                    https: true
                },
                callback
            );
        };

        const payment = new Payment({
            partnerKey: wxPay.key,
            appId: this.state.wxa.authorizer_appid,
            mchId: wxPay.mchid,
            notifyUrl: notifyUrl,
            pfx: Buffer.from(JSON.parse(wxPay.pfx).data)
        });

        try {
            const payargs = await new Promise((resolve, reject) => {
                payment.transferCreateUnifiedOrder(
                    {
                        partner_trade_no: partner_trade_no,
                        amount: parseInt(parseFloat(amount) * 100),
                        spbill_create_ip: "127.0.0.1",
                        openid: openid,
                        check_name: "FORCE_CHECK",
                        re_user_name: re_user_name,
                        desc: "提现"
                    },
                    (err, payargs) => {
                        if (err) return reject(err);
                        resolve(payargs);
                    }
                );
            });

            this.success(payargs, "成功");
        } catch (err) {
            this.fail(err.message, "失败");
        }
    }
};
